package plus.lgx.textsearcher.config;

import plus.lgx.textsearcher.searcher.extender.ExtenderEnum;
import plus.lgx.textsearcher.util.PathUtil;

import java.io.File;
import java.util.Comparator;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * <p>搜索器的配置类</p>
 *
 * Create time: 12/17/22 7:27 PM
 *
 * @author lgx
 * @version 1.0
 */
public class SearcherConfig {

    /**
     * 默认配置
     */
    public static class DefaultSearcherConfig {

        public static final int SHOW_SKIPPED = 20;

        public static final int MAX_EXTEND_CHARS_COUNT = 140;

        public static final ExtenderEnum EXTENDER_ENUM = ExtenderEnum.DEFAULT;

        public static final int LINES = 1;

        public static final int BUFFER_SIZE = 4 * 1024 * 1024;

        public static final Pattern WHITELIST = null;

        public static final Pattern BLACKLIST = null;

        public static final char[][] DELIMITER = new char[3][];

        public static final boolean SERVER = false;

        public static final boolean CONSOLE = false;

        public static final boolean NO_PAUSE = false;

        public static final boolean HTML = false;

        public static final boolean IGNORE_CASE = false;

        public static final int PORT = 18000;

        static {
            DELIMITER[0] = "　。！？.\n".toCharArray();
            DELIMITER[1] = "「".toCharArray();
            DELIMITER[2] = "」".toCharArray();
        }

        public static final boolean RANDOMIZE = false;
        public static final String SORT0 = "none";

        public static final Comparator<File> SORT = null;

        public static final String OUTPUT_FILE_NAME = "search_result.txt";
        public static final String OUTPUT_FILE_NAME_HTML = "search_result.html";

        public static final String SEARCH_DIRECTORY_PATH = "";

        public static final int MAX_FILE_COUNT = -1;

    }

    /**
     * 显示跳过的文件的数量
     */
    public final int showSkipped;

    /**
     * 匹配的字符串向前后扩展的最大字符数
     */
    public final int maxExtendCharsCount;

    /**
     * 匹配的字符串向前后扩展的方式
     */
    public final ExtenderEnum extenderEnum;

    /**
     * 如果是多行扩展的方式，此变量指定扩展的行数
     */
    public final Integer lines;

    /**
     * 从输入的文件中读取的缓冲区大小
     */
    public final int bufferSize;

    /**
     * 文件名白名单
     */
    public final Pattern whitelist;

    /**
     * 文件名黑名单
     */
    public final Pattern blacklist;

    /**
     * 分隔符
     */
    public final char[][] DELIMITER = new char[3][];

    /**
     * 是否随机化文件顺序
     */
    public final boolean randomize;

    /**
     * 排序方式
     */
    public final Comparator<File> sort;

    /**
     * 是否开启服务器
     */
    public final boolean server;

    /**
     * 如果开启服务器，则服务器的端口号在此配置
     */
    public final int port;

    /**
     * 输出的文件路径
     */
    public final String outputFileName;

    /**
     * 待搜索的目录
     */
    public final String searchDirectoryPath;

    /**
     * 搜索的内容
     */
    public final String searchContent;

    /**
     * 指定输出的模板文件的路径
     */
    public final String template;

    /**
     * 是否在控制台输出
     */
    public final boolean console;

    /**
     * 在控制台输出时，是否取消间隔性的暂停输出
     */
    public final boolean noPause;

    /**
     * 是否以 HTML 的形式输出，若指定 template，则该选项会被忽略
     */
    public final boolean html;

    /**
     * 是否忽略大小写
     */
    public final boolean ignoreCase;

    /**
     * 在待搜索的文本中要忽略的内容，由正则表达式表示
     */
    public final String skip;

    /**
     * 当指定了在待搜索的文本中要忽略的内容时，搜索的内容使用正则表达式
     */
    public final Pattern searchContentRegex;

    /**
     * 最大的文件搜索数量
     */
    public final int maxFileCount;

    public SearcherConfig(
            Integer showSkipped,   Integer maxExtendCharsCount, ExtenderEnum extenderEnum,
            Integer lines,         Integer bufferSize,          Pattern whitelist,
            Pattern blacklist,     char[][] DELIMITER,          Boolean randomize,
            String outputFileName, String searchDirectoryPath,  String searchContent,
            Comparator<File> sort, Boolean server,              Integer port,
            String template,       boolean console,             boolean noPause,
            boolean html,          boolean ignoreCase,          String skip,
            int maxFileCount
    ) {
        this.showSkipped =    Optional.ofNullable(showSkipped) .orElse(DefaultSearcherConfig.SHOW_SKIPPED);
        this.extenderEnum =   Optional.ofNullable(extenderEnum).orElse(DefaultSearcherConfig.EXTENDER_ENUM);
        this.bufferSize =     Optional.ofNullable(bufferSize)  .orElse(DefaultSearcherConfig.BUFFER_SIZE);
        this.whitelist =      Optional.ofNullable(whitelist)   .orElse(DefaultSearcherConfig.WHITELIST);
        this.blacklist =      Optional.ofNullable(blacklist)   .orElse(DefaultSearcherConfig.BLACKLIST);
        this.DELIMITER[0] =   Optional.ofNullable(DELIMITER[0]).orElse(DefaultSearcherConfig.DELIMITER[0]);
        this.DELIMITER[1] =   Optional.ofNullable(DELIMITER[1]).orElse(DefaultSearcherConfig.DELIMITER[1]);
        this.DELIMITER[2] =   Optional.ofNullable(DELIMITER[2]).orElse(DefaultSearcherConfig.DELIMITER[2]);
        this.randomize =      Optional.ofNullable(randomize)   .orElse(DefaultSearcherConfig.RANDOMIZE);
        this.sort =           Optional.ofNullable(sort)        .orElse(DefaultSearcherConfig.SORT);
        this.port =           Optional.ofNullable(port)        .orElse(DefaultSearcherConfig.PORT);
        this.server =         Optional.ofNullable(server)      .orElse(DefaultSearcherConfig.SERVER);
        this.lines =          Optional.ofNullable(lines)       .orElse(DefaultSearcherConfig.LINES);
        this.outputFileName = Optional.ofNullable(outputFileName).orElse(
                html ? DefaultSearcherConfig.OUTPUT_FILE_NAME_HTML
                     : DefaultSearcherConfig.OUTPUT_FILE_NAME
        );
        this.maxExtendCharsCount =
                Optional.ofNullable(maxExtendCharsCount).orElse(DefaultSearcherConfig.MAX_EXTEND_CHARS_COUNT);
        String s = Optional.ofNullable(searchDirectoryPath).orElse(DefaultSearcherConfig.SEARCH_DIRECTORY_PATH);
        this.searchDirectoryPath = s.isBlank() ? PathUtil.standardize(".", true) : s;
        this.searchContent =  searchContent;
        this.template = template;
        this.console = console;
        this.noPause = noPause;
        this.html = html;
        this.ignoreCase = ignoreCase;
        this.skip = skip;

        // 如果指定了要忽略的内容，则构建搜索内容的正则式
        if (skip != null) {
            String skip1 = skip.startsWith("(") && skip.endsWith(")?") && !skip.endsWith("\\)?")
                    ? skip
                    : skip.startsWith("(") && skip.endsWith(")") && !skip.endsWith("\\)")
                            ? skip + '?'
                            : '(' + skip + ")?";
            StringBuilder sb = new StringBuilder();
            char[] chs = searchContent.toCharArray();
            for (int i = 0, l = chs.length - 1; ; i++) {
                sb.append(switch (chs[i]) {
                    case '{', '}', '[', ']', '(', ')', '+', '|',
                         '^', '$', '.', '?', '*', '\\' -> "\\" + chs[i];
                    default -> chs[i];
                });
                if (i == l) break;
                sb.append(skip1);
            }
            searchContentRegex = ignoreCase
                    ? Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE)
                    : Pattern.compile(sb.toString());
        } else {
            searchContentRegex = null;
        }

        this.maxFileCount = maxFileCount;
    }

}
